package com.picshare.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.picshare.model.FileItem;
import com.picshare.model.PageResult;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FileService {
    
    @Value("${app.data.dir}")
    private String dataDir;
    
    @Value("${app.albums.dir}")
    private String albumsDir;
    
    @Value("${app.thumbnails.dir}")
    private String thumbnailsDir;
    
    @Value("${app.temp.dir}")
    private String tempDir;
    
    private static final List<String> IMAGE_EXTENSIONS = Arrays.asList(
            "jpg", "jpeg", "png", "gif", "webp", "bmp", "tiff", "svg", "heic", "heif"
    );
    
    private final ObjectMapper objectMapper = new ObjectMapper();
    private File dataFile;
    private List<FileItem> files = new ArrayList<>();
    
    @Autowired
    private ShareService shareService;
    
    @Autowired
    private StreamListenerService streamListenerService;
    
    @PostConstruct
    public void init() {
        createDirectories();
        dataFile = new File(dataDir, "files.json");
        loadFiles();
        migrateFileHashes();
    }
    
    private void createDirectories() {
        try {
            Files.createDirectories(Paths.get(dataDir));
            Files.createDirectories(Paths.get(albumsDir));
            Files.createDirectories(Paths.get(thumbnailsDir));
            Files.createDirectories(Paths.get(tempDir));
        } catch (IOException e) {
            System.err.println("创建目录失败: " + e.getMessage());
        }
    }
    
    private void migrateFileHashes() {
        for (FileItem file : files) {
            if (file.getFileHash() == null || file.getFileHash().isEmpty()) {
                try {
                    Path filePath = Paths.get(albumsDir, file.getFileName());
                    if (Files.exists(filePath)) {
                        byte[] fileBytes = Files.readAllBytes(filePath);
                        String fileHash = calculateHashFromBytes(fileBytes);
                        file.setFileHash(fileHash);
                    }
                } catch (Exception e) {
                    System.err.println("无法为文件 " + file.getFileName() + " 计算哈希值: " + e.getMessage());
                }
            }
        }
        saveFiles();
    }
    
    private String calculateHashFromBytes(byte[] fileBytes) throws NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] hashBytes = digest.digest(fileBytes);
        
        StringBuilder hexString = new StringBuilder();
        for (byte b : hashBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        
        return hexString.toString();
    }
    
    private void loadFiles() {
        if (dataFile.exists()) {
            try {
                FileItem[] fileArray = objectMapper.readValue(dataFile, FileItem[].class);
                files = new ArrayList<>(Arrays.asList(fileArray));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    private void saveFiles() {
        try {
            objectMapper.writerWithDefaultPrettyPrinter().writeValue(dataFile, files);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    private String calculateFileHash(MultipartFile file) throws IOException, NoSuchAlgorithmException {
        MessageDigest digest = MessageDigest.getInstance("SHA-256");
        byte[] fileBytes = file.getBytes();
        byte[] hashBytes = digest.digest(fileBytes);
        
        StringBuilder hexString = new StringBuilder();
        for (byte b : hashBytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        
        return hexString.toString();
    }
    
    public boolean isFileDuplicate(String fileHash) {
        return files.stream().anyMatch(file -> fileHash.equals(file.getFileHash()));
    }
    
    public FileItem findFileByHash(String fileHash) {
        return files.stream().filter(file -> fileHash.equals(file.getFileHash())).findFirst().orElse(null);
    }
    
    public FileItem uploadFile(MultipartFile file, String uploader) throws IOException {
        try {
            String fileHash = calculateFileHash(file);
            
            if (isFileDuplicate(fileHash)) {
                return null;
            }
            
            String originalName = file.getOriginalFilename();
            String extension = getFileExtension(originalName);
            String fileName = UUID.randomUUID().toString() + "." + extension;
            
            Path targetPath = Paths.get(albumsDir, fileName);
            Files.copy(file.getInputStream(), targetPath);
            
            boolean isImage = isImageFile(extension);
            if (isImage) {
                createThumbnail(fileName, extension);
            }
            
            FileItem fileItem = new FileItem();
            fileItem.setId(UUID.randomUUID().toString());
            fileItem.setOriginalName(originalName);
            fileItem.setFileName(fileName);
            fileItem.setSize(file.getSize());
            fileItem.setType(extension);
            fileItem.setImage(isImage);
            fileItem.setUploadTime(System.currentTimeMillis());
            fileItem.setUploader(uploader);
            fileItem.setFileHash(fileHash);
            
            files.add(fileItem);
            saveFiles();
            
            // 发布照片上传事件用于SSE推送
            streamListenerService.publishPhotoUploaded(fileItem);
            
            return fileItem;
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            throw new IOException("无法计算文件哈希值", e);
        }
    }
    
    public void deleteFile(String id) throws IOException {
        FileItem fileItem = getFileById(id);
        if (fileItem != null) {
            Path filePath = Paths.get(albumsDir, fileItem.getFileName());
            Path thumbnailPath = Paths.get(thumbnailsDir, fileItem.getFileName());
            
            Files.deleteIfExists(filePath);
            Files.deleteIfExists(thumbnailPath);
            
            // 清理该文件相关的所有分享记录
            shareService.deleteSharesByFileId(id);
            
            files.removeIf(f -> f.getId().equals(id));
            saveFiles();
        }
    }
    
    public FileItem renameFile(String id, String newName) throws IOException {
        FileItem fileItem = getFileById(id);
        if (fileItem != null) {
            fileItem.setOriginalName(newName);
            saveFiles();
            return fileItem;
        }
        return null;
    }
    
    public List<FileItem> searchFiles(String keyword) {
        if (keyword == null || keyword.trim().isEmpty()) {
            return new ArrayList<>(files);
        }
        
        return files.stream()
                .filter(f -> f.getOriginalName().toLowerCase().contains(keyword.toLowerCase()))
                .sorted((a, b) -> Long.compare(b.getUploadTime(), a.getUploadTime()))
                .collect(Collectors.toList());
    }
    
    public PageResult<FileItem> searchFilesPage(String keyword, int page, int size) {
        List<FileItem> searchResults = searchFiles(keyword);
        int totalItems = searchResults.size();
        int totalPages = (int) Math.ceil((double) totalItems / size);
        
        // 确保页码在有效范围内
        page = Math.max(1, Math.min(page, totalPages));
        
        int startIndex = (page - 1) * size;
        int endIndex = Math.min(startIndex + size, totalItems);
        
        List<FileItem> pageFiles = searchResults.subList(startIndex, endIndex);
        
        return new PageResult<>(pageFiles, page, size, totalItems);
    }
    
    public List<FileItem> getAllFiles() {
        return files.stream()
                .sorted((a, b) -> Long.compare(b.getUploadTime(), a.getUploadTime()))
                .collect(Collectors.toList());
    }
    
    public PageResult<FileItem> getFilesPage(int page, int size) {
        List<FileItem> allFiles = getAllFiles();
        int totalItems = allFiles.size();
        int totalPages = (int) Math.ceil((double) totalItems / size);
        
        // 确保页码在有效范围内
        page = Math.max(1, Math.min(page, totalPages));
        
        int startIndex = (page - 1) * size;
        int endIndex = Math.min(startIndex + size, totalItems);
        
        List<FileItem> pageFiles = allFiles.subList(startIndex, endIndex);
        
        return new PageResult<>(pageFiles, page, size, totalItems);
    }
    
    public FileItem getFileById(String id) {
        return files.stream()
                .filter(f -> f.getId().equals(id))
                .findFirst()
                .orElse(null);
    }
    
    private String getFileExtension(String fileName) {
        if (fileName == null || fileName.lastIndexOf(".") == -1) {
            return "";
        }
        return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
    }
    
    private boolean isImageFile(String extension) {
        return IMAGE_EXTENSIONS.contains(extension.toLowerCase());
    }
    
    private void createThumbnail(String fileName, String extension) throws IOException {
        Path sourcePath = Paths.get(albumsDir, fileName);
        Path thumbnailPath = Paths.get(thumbnailsDir, fileName);
        
        try {
            Thumbnails.of(sourcePath.toFile())
                    .size(300, 300)
                    .keepAspectRatio(true)
                    .toFile(thumbnailPath.toFile());
        } catch (Exception e) {
            Files.copy(sourcePath, thumbnailPath);
        }
    }
}